package com.lwl.concurrency.thread;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <pre>
 * object.wait() / object.notify() / object.notifyAll()方法必须获取到监视器才能正确执行
 *    synchronized (object){
 *       object.notifyAll();
 *    }
 * 在一个线程中一个对象获取了监视器以后,调用了wait()方法会阻塞,并且释放监视器,
 * 多个线程调用对象的wait()方法,会进入对象的等待队列,
 * 在另一个线程中调用一次notify()就会从等待队列中随机选择一个线程继续执行,可以多次调用.
 * 调用对象的notifyAll()让等待队列的所有线程继续执行
 * 调用对象的wait(),必须由相同对象调用 notify() 或者 notifyAll(),notify方法必须在wait后调用
 *
 * object.wait()和 Thread.sleep()方法都可以让线程等待若干时间,
 * 但是wait()方法可以被唤醒,并且释放目标对象的锁
 * Thread.sleep()方法不会释放任何资源.
 * </pre>
 *
 * @author liwenlong - 2018/3/25 19:45
 */
public class SimpleWaitNotify {
    ReentrantLock lock = new ReentrantLock();

    private final Object object = new Object();

    private final Object object2 = new Object();

    public static void main(String[] args) throws InterruptedException {
        SimpleWaitNotify simpleWaitNotify = new SimpleWaitNotify();
        simpleWaitNotify.test1();
    }

    public void test1() throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            ObjectWaitThread wait = new ObjectWaitThread();
            wait.setName("ObjectWaitThread--" + i);
            wait.start();
        }

       /* for (int i = 0; i < 2; i++) {
            Object2WaitThread object2WaitThread = new Object2WaitThread();
            object2WaitThread.setName("object2WaitThread--"+i);
            object2WaitThread.start();
        }*/
        //notify方法必须在wait后调用
        TimeUnit.SECONDS.sleep(3);
        synchronized (object) {
            // object.notifyAll();
            object.notify();
            object.notify();
        }

        /*for (int i = 0; i < 2 ; i++) {
            TimeUnit.SECONDS.sleep(3);
            ObjectNotifyThread notify = new ObjectNotifyThread();
            notify.setName("notify-thread--"+i);
            notify.start();
        }*/
    }

    /**
     * 调用对象 wait() 线程
     */
    public class Object2WaitThread extends Thread {
        @Override
        public void run() {
            synchronized (object2) {
                System.out.println(Thread.currentThread().getName() + ": start! " + LocalDateTime.now().toString());
                try {
                    System.out.println(Thread.currentThread().getName() + ": wait for object!");
                    //object2.wait();
                    object2.wait(50000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ": end wait!" + LocalDateTime.now().toString());
            }
        }
    }

    /**
     * 调用对象 wait() 线程
     */
    public class ObjectWaitThread extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + ":T1 start! " + LocalDateTime.now().toString());
                try {
                    System.out.println(Thread.currentThread().getName() + ":T1 wait for object!");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":T1 end wait!" + LocalDateTime.now().toString());
            }
        }
    }

    /**
     * 调用对象 notify() 线程
     */
    public class ObjectNotifyThread extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + ":T1 notify start! " + LocalDateTime.now().toString());
                object.notify();
                System.out.println(Thread.currentThread().getName() + ":T1 notify end!" + LocalDateTime.now().toString());
            }
        }
    }


}
